/*
 *    Copyright 2014-2017 Alexey Danilov
 *    Copyright 2021 Institute of Software Chinese Academy of Sciences, ISRC

 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */
package com.danikula.videocache;

import com.danikula.videocache.defaultStrategy.FileCache;
import com.danikula.videocache.defaultStrategy.HttpProxyCache;
import com.danikula.videocache.interfacers.CacheListener;
import com.danikula.videocache.util.LogUtil;
import ohos.eventhandler.EventHandler;
import ohos.eventhandler.EventRunner;
import ohos.eventhandler.InnerEvent;

import java.io.File;
import java.io.IOException;
import java.net.Socket;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.atomic.AtomicInteger;

import static com.danikula.videocache.Preconditions.checkNotNull;

/**
 * Client for {@link HttpProxyCacheServer}
 *
 * @author Alexey Danilov (danikula@gmail.com).
 */
final class HttpProxyCacheServerClients {
    private final AtomicInteger clientsCount = new AtomicInteger(0);
    private final String url;
    private volatile ProxyCache proxyCache;
    private final List<CacheListener> listeners = new CopyOnWriteArrayList<>();
    private final CacheListener uiCacheListener;
    private final Config config;
    private final CacheStrategy cacheStrategy;

    HttpProxyCacheServerClients(String url, Config config, CacheStrategy cacheStrategy) {
        this.url = checkNotNull(url);
        this.config = checkNotNull(config);
        this.uiCacheListener = new UiListenerHandler(url, listeners);
        this.cacheStrategy = checkNotNull(cacheStrategy);
    }

    /**
     * 开启缓存线程
     * 开始读写视频数据
     *
     * @param request 请求信息
     * @param socket  客户端socket
     * @throws ProxyCacheException  缓存代理错误
     * @throws IOException          IO错误
     * @throws InterruptedException 线程中断错误
     */
    public void processRequest(GetRequest request, Socket socket) throws ProxyCacheException, IOException,
            InterruptedException {
        startProcessRequest();
        try {
            clientsCount.incrementAndGet();
            proxyCache.processRequest(request, socket);
        } finally {
            finishProcessRequest();
        }
    }

    private synchronized void startProcessRequest() throws ProxyCacheException {
        proxyCache = proxyCache == null ? newHttpProxyCache() : proxyCache;
    }

    private synchronized void finishProcessRequest() {
        try {
            if (proxyCache == null || clientsCount == null) {
                return;
            }
            proxyCache.doComplete();
            if (clientsCount.decrementAndGet() <= 0) {
                proxyCache.shutdown();
                proxyCache = null;
            }
        } catch (ProxyCacheException e) {
            LogUtil.error(this.getClass().getSimpleName(), e.toString());
        }
    }

    /**
     * 注册缓存进度监听
     *
     * @param cacheListener 缓存进度监听
     */
    public void registerCacheListener(CacheListener cacheListener) {
        listeners.add(cacheListener);
    }

    /**
     * 注销缓存进度监听
     *
     * @param cacheListener 缓存进度监听
     */
    public void unregisterCacheListener(CacheListener cacheListener) {
        listeners.remove(cacheListener);
    }

    /**
     * 关闭缓存代理实现
     * 注销缓存进度监听
     */
    public void shutdown() {
        listeners.clear();
        if (proxyCache != null) {
            proxyCache.registerCacheListener(null);
            proxyCache.shutdown();
            proxyCache = null;
        }
        clientsCount.set(0);
    }

    public int getClientsCount() {
        return clientsCount.get();
    }

    private ProxyCache newHttpProxyCache() throws ProxyCacheException {
        HttpUrlSource source = new HttpUrlSource(url, config.sourceInfoStorage, config.headerInjector);
        return cacheStrategy.createHttpProxyCache(source, config, uiCacheListener, url);
    }

    private static final class UiListenerHandler extends EventHandler implements CacheListener {
        private final String url;
        private final List<CacheListener> listeners;

        UiListenerHandler(String url, List<CacheListener> listeners) {
            super(EventRunner.getMainEventRunner());
            this.url = url;
            this.listeners = listeners;
        }

        @Override
        public void onCacheAvailable(File file, String url, int percentsAvailable) {
            InnerEvent event = InnerEvent.get();
            event.sendingUid = percentsAvailable;
            event.object = file;
            event.eventId = 1024;
            sendEvent(event);
        }

        @Override
        public void onCachePartAvailable(File cacheFile, String url, List<long[]> percentsAvailables, long fileLength) {
            InnerEvent event = InnerEvent.get();
            event.object = percentsAvailables;
            event.eventId = 1025;
            event.sendingUid = (int) fileLength;
            sendEvent(event);
        }

        @Override
        public void processEvent(InnerEvent event) {
            if (event.eventId == 1024) {
                for (CacheListener cacheListener : listeners) {
                    cacheListener.onCacheAvailable((File) event.object, url, event.sendingUid);
                }
            } else {
                for (CacheListener cacheListener : listeners) {
                    cacheListener.onCachePartAvailable(null, url, (List<long[]>) event.object, event.sendingUid);
                }
            }
        }
    }
}